#pragma once
#include <cassert>
#include <iostream>
#include <algorithm>
#include <functional>
#include "CPlayer.hpp"

/// @cond EXCLUDE
// 他の翻訳単位もテンプレートを実体化出来るようにする
class CPlayers::Impl
{
	friend class CPlayers;
	// for CPlayers::foreach()
	// メンバ変数ポインタとテンプレートでゴリ押しとか訳がわからないよ
	template<class Fn, class Container, class ParentContainer>
	class ForeachProxy
	{
	public:
		typedef ParentContainer PC;
		typedef Container C;

	protected:
		Fn& function;
		C PC::*mem_ptr;

	public:
		ForeachProxy(Fn& func, C PC::* member_ptr)
			: function(func), mem_ptr(member_ptr)
		{
		}

		void operator()(PC* pc)
		{
			assert(pc != NULL);
			C& c = pc->*mem_ptr;
			typename C::iterator it = c.begin();
			while(it != c.end())
			{
				function(*it++);
			}
		/*	std::for_each(
				c.begin(),
				c.end(),
				function);*/
		}
	};

	template<class Fn, class Container, class ParentContainer>
	class ForeachContainerProxy
	{
	public:
		typedef ParentContainer PC;
		typedef Container C;
	
	private:
		Fn& function;
		C PC::*mem_ptr;
		
	public:
		ForeachContainerProxy(Fn& func, C PC::*member_ptr)
			: function(func), mem_ptr(member_ptr)
		{
		}

		void operator()(PC* pc)
		{
			assert(pc != NULL);
			C& c = pc->*mem_ptr;
			function(&c);
		}
	};

	template<
		template<class T1, class T2, class T3>	class T,
		class Fn, class Container, class ParentContainer>
	static T<Fn, Container, ParentContainer> MakeProxy(
		Fn& function, Container ParentContainer::* ptr)
	{
		return T<Fn, Container, ParentContainer>(function, ptr);
	}

	template<class Fn, class T>
	struct ForeachAdaptor
	{
		static void Func(Fn& f, CPlayers& p)
		{
			p.foreach<typename T::Parent>(
				MakeProxy<ForeachProxy>(f, T::getParent()));
		}
	};

	template<class Fn, class T>
	struct ForeachAdaptor<Fn, ptr_vector<T> >
	{
		static void Func(Fn& f, CPlayers& p)
		{
			p.foreach<typename T::Parent>(
				MakeProxy<ForeachContainerProxy>(
					f, T::getParent()));
		}
	};

	template<class Fn>
	struct ForeachAdaptor<Fn, CPlayer>
	{
		static void Func(Fn& f, CPlayers& p)
		{
			std::for_each(p.begin(), p.end(), f);
		}
	};

	template<class Fn>
	struct ForeachAdaptor<Fn, ptr_vector<CPlayer> >
	{
		static void Func(Fn& f, CPlayers& p)
		{
			f(&p);
		}
	};

	template<class Fn>
	struct ForeachAdaptor<Fn, CPlayers>
	{
		static void Func(Fn& f, CPlayers& p)
		{
			f(&p);
		}
	};
	
	template<class T, class It, class Fn>
	struct CopyProxy
	{
		Fn& f;
		It& i;
		
		CopyProxy(It& iterator, Fn& func)
			: f(func), i(iterator)
		{
		}

		void operator()(T* instance)
		{
			*i = f(instance);
			i++;
		}
	};
	
	template<class T>
	static T* copy_default_func(T* t)
	{
		return t; 
	}
};
/// @endcond

/**
* @brief Copy all element
*
* @c func must return assignable for @c out.
*
* @tparam T Target class
* @tparam Iterator Output-iterator
* @tparam Fn Function
* @param out Iterator for output (Usually, use std::inserter)
* @param func User-defined function for transforming.
*
* @return Iterator of the last) element.
*/
template<class T, class It, class Fn>
inline void CPlayers::copy(It out, Fn func)
{
	foreach<T>(Impl::CopyProxy<T, It, Fn>(out, func));
}

/// @copydoc CPlayers::copy(It,Fn)
template<class T, class It>
inline void CPlayers::copy(It out)
{
	typedef T*(*Fn)(T*);
	Fn f = &Impl::copy_default_func;
	foreach<T>(Impl::CopyProxy<T, It, Fn>(out, f));
}

/**
* @brief Foreach
*
* This function helps to access all element that are managed by each different instances.
*
* @par Function specification
* function is must be the functor which has one argument.
* Return is ignored.
* @code
* void function(T* t){} // func (use via std::ptr_fun)
* struct Function { void operator()(T* t){} }; // functor
* @endcode
*
* @par Example
* @code
* // This code operate as
* // p.Players[i]->Mobs.clear();
* p.foreach<CMobs>(std::mem_fun(&CMob::clear));
*
* // p.Players[i]->Mobs[j]->Ops[k]->getId();
* p.foreach<COperation>(std::mem_fun(&COperation::getId))
* @endcode
*
* @tparam T Property to apply function
* @tparam Fn Functor
* @param function Functor to pass to foreach.
*/
template<class T, class Fn>
inline void CPlayers::foreach(Fn function)
{
	// FnとTの位置を入れ替えるのがポイント
	Impl::ForeachAdaptor<Fn, T>::Func(function, *this);

	
	// クラス宣言部でtemplate<T, Fn>としようとすると第一テンプレート引数がかわるため
	// p.foreach<CMob>(func);
	// みたいな書き方ができなくなる。こうなるとFnを明示しなければならなくなって冗長なのでプロキシ関数をかませる。
}

